#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/gtx/string_cast.hpp>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include "camera.h"

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

//for temporary stbi_load
unsigned char *data;

// camera
Camera camera(glm::vec3(1.0f, 1.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;

// timing
float deltaTime = 0.0f;	
float lastFrame = 0.0f;

// lighting
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

bool firstMouse = true;

static int complieShader(unsigned int type, const std::string& source)
{
    unsigned int id = glCreateShader(type);

    const char* src = source.c_str();
    /**pass the GLSL source code into the shader*/
    glShaderSource(id, 1, &src, nullptr);
    /**compile the source code*/
    glCompileShader(id);

    // TODO: Error handling 
    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if(result == false)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id , length, &length, message);
        std::cout << "fail to complie shader : "<<
        ((type == GL_VERTEX_SHADER)?"GL_VERTEX_SHADER":"GL_FRAGMENT_SHADER")<< 
        message << std::endl;

        glDeleteShader(id);
        return 0;
    }
    return id;
}


static unsigned int createShader(const std::string& vertex_shader, 
                        const std::string& fragment_shader)
{
    /**complie the GLSL code*/
    unsigned int vs = complieShader(GL_VERTEX_SHADER, vertex_shader);
    unsigned int fs = complieShader(GL_FRAGMENT_SHADER, fragment_shader);
    
    unsigned int program = glCreateProgram(); 
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);

    glValidateProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}
// int getPointListSize(unsigned int index_list[][4]){
//     int cur_point_size = sizeof(index_list) / 4;
//     return cur_point_size;
// }
#define getPointListSize(index_list) \
    sizeof(index_list) / sizeof(int)

#define  mallocBuffer_pct( vertex_buffer, vertex_array, positions) \
    int stride = (3+3+2);\
    glGenVertexArrays(1, &vertex_array);\
    glGenBuffers(1,&vertex_buffer);\
    \
    glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer);\
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);\
    \
    glBindVertexArray(vertex_array);\
    /**position attribute*/\
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)0);\
    glEnableVertexAttribArray(0);\
    /**color attribute*/\
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(3* sizeof(float)));\
    glEnableVertexAttribArray(1);\
    /* texture coord attribute */\
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(6 * sizeof(float)));\
    glEnableVertexAttribArray(2);\





#define  mallocBuffer( vertex_buffer, vertex_array, element_buffer, positions, index_list) \
    int stride = (7+2);\
    glGenVertexArrays(1, &vertex_array);\
    glGenBuffers(1,&vertex_buffer);\
    glGenBuffers(1,&element_buffer);\
    \
    glBindVertexArray(vertex_array);\
    \
    glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer);\
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);\
    \
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);\
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_list), index_list, GL_STATIC_DRAW);\
    \
    /**position attribute*/\
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)0);\
    glEnableVertexAttribArray(0);\
    /**color attribute*/\
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(3* sizeof(float)));\
    glEnableVertexAttribArray(1);\
    /* texture coord attribute */\
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(7 * sizeof(float)));\
    glEnableVertexAttribArray(2);

#define renderingTexture(texture1, image_dir, width, height, nrChannels)\
    glGenTextures(1, &texture1);\
    glBindTexture(GL_TEXTURE_2D, texture1);\
    /*set the texture wrapping parameters*/\
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\
    /*set texture filtering parameters*/\
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\
    /*load image, create texture and generate mipmaps*/\
    stbi_set_flip_vertically_on_load(true); \
    data = stbi_load(image_dir, &width, &height, &nrChannels, 0);\
    if (data)\
    {\
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\
        glGenerateMipmap(GL_TEXTURE_2D);\
    }\
    else\
    {\
        std::cout << "Failed to load texture" << std::endl;\
    }\
    stbi_image_free(data);


#define renderingObj(vertex_array, positions, texture, index_list)\
        glActiveTexture(GL_TEXTURE0);\
        glBindTexture(GL_TEXTURE_2D, texture);\
        \
        glBindVertexArray(vertex_array);\
        glEnableClientState(GL_VERTEX_ARRAY);\
        glVertexPointer(3, GL_FLOAT, 0, positions);\
        /*渲染点的个数*/\
        glDrawElements(GL_QUADS, getPointListSize(index_list), GL_UNSIGNED_INT, 0);\


#define renderingObj_pct(vertex_array, texture)\
        glActiveTexture(GL_TEXTURE0);\
        glBindTexture(GL_TEXTURE_2D, texture);\
        \
        glBindVertexArray(vertex_array);\
        glDrawArrays(GL_TRIANGLES, 0, 36);


int main()
{
    // glfw: initialize and configure
    glfwInit();
    //调用版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    //调用功能 core不含shader
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // glad: load all OpenGL function pointers
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /**location of the lower left corner, set the width and height of the rendering window*/
    glEnable(GL_DEPTH_TEST);  

    // set up vertex data (and buffer(s)) and configure vertex attributes


    /**
     * y
     * |
     * |
     * -------->x
     * z to you!
    */
   float normal_cube_position[] = {
        /**fore-head*/
        -1.0f, -1.0f, 1.0f,  123.0f/255, 104.0f/255, 238.0f/255, 1.0f,  1.0f, 1.0f,  0.0f,  0.0f, -1.0f,//!bottom-left  0
        -1.0f,  1.0f, 1.0f,  1.0f,       1.0f,       0.0f,       1.0f,  1.0f, 0.0f,  0.0f,  0.0f, -1.0f,//!upper -left  1
         1.0f,  1.0f, 1.0f,  1.0f,       1.0f,       1.0f,       1.0f,  0.0f, 0.0f,  0.0f,  0.0f, -1.0f,//!upper -right 2
         1.0f, -1.0f, 1.0f,  1.0f,       0.0f,       0.0f,       1.0f,  0.0f, 1.0f,  0.0f,  1.0f, 0.0f,//!bottom-right 3
        /**back-head*/
        -1.0f, -1.0f,-1.0f,  0.0f,       1.0f,       1.0f,       1.0f,  1.0f, 1.0f,  0.0f,  1.0f, 0.0f,//!bottom-left  4
        -1.0f,  1.0f,-1.0f,  0.0f,       1.0f,       0.0f,       1.0f,  1.0f, 0.0f,  0.0f,  1.0f, 0.0f,//!upper -left  5
         1.0f,  1.0f,-1.0f,  127.0f/255, 255.0f/255, 212.0f/255, 1.0f,  0.0f, 0.0f,  1.0f,  0.0f, 0.0f,//!upper -right 6
         1.0f, -1.0f,-1.0f,  0.0f,       0.0f,       1.0f,       1.0f,  0.0f, 1.0f,  1.0f,  0.0f, 0.0f,//!bottom-right 7

   };



    unsigned int index_list[][4] = {
        0, 3, 2, 1,
        0, 3, 7, 4,
        0, 1, 5, 4,
        6, 2, 3, 7,
        6, 5, 1, 2,
        6, 7, 4, 5,
    };


    float vertices[] = {
        // positions          // normals           // texture coords
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
    };

    // int cur_point_size = sizeof(index_list) / sizeof(int);
    // std::cout << "1: " << getPointListSize(left_arm_list) << " 2: " << cur_point_size << std::endl;

    // /**allocate memory and bind the vertexs' postion to it*/
    unsigned int vertex_buffer;//!store all points
    unsigned int vertex_array; //!as a array
    unsigned int element_buffer; //! pointers for fetching the points
    //mallocBuffer(vertex_buffer, vertex_array, element_buffer, normal_cube_position, index_list);

    mallocBuffer_pct( vertex_buffer, vertex_array, vertices);

    //load in texture
    unsigned int texture1, texture2; 

    int width, height, nrChannels;
    renderingTexture(texture1, "../textures/wood.jpg", width, height, nrChannels);
    renderingTexture(texture2, "../textures/face.jpg", width, height, nrChannels);


    Shader ourShader("../shaders/transform.vs", 
                     "../shaders/transform.fs");

    Shader Mylight("../shaders/basic_lighting.vs", 
                   "../shaders/basic_lighting.fs");

    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
    // render loop
    // -----------
            // note that we're translating the scene in the reverse direction of where we want to move
        /* y
           |
           |
           ---------------》x
         */
    glm::mat4 view = glm::mat4(1.0f);
    glm::mat4 projection;
    projection = glm::perspective(glm::radians(60.0f), 800.0f / 600.0f, 0.1f, 100.0f);
        /**
         * glm::ortho(float left, float right, float bottom, float top, float zNear, float zFar); 
         *                 前两个参数指定了平截头体的左右坐标，
         *                 第三和第四参数指定了平截头体的底部和顶部。
         *                 通过这四个参数我们定义了近平面和远平面的大小，
         *                 然后第五和第六个参数则定义了近平面和远平面的距离。
         * 
           glm::perspective(float fovy, float aspect, float zNear, float zFar); 
                            第一个参数为视锥上下面之间的夹角 : fovy 是弧度
                            第二个参数为宽高比，即视窗的宽/高，
                            第三第四个参数分别为近截面和远界面的深度
         * 
        */
        

    view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 

    int viewLoc = glGetUniformLocation(ourShader.ID, "view");
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        
    int projectionLoc = glGetUniformLocation(ourShader.ID, "projection");
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
    
    glm::vec3 head_size = glm::vec3(0.3f, 0.3f, 0.3f);
    glm::vec3 body_size = glm::vec3(0.5f, 0.5f, 0.5f);
    glm::vec3 arm_size = glm::vec3(0.1f, 0.5f, 0.1f);


    // shader configuration
    // --------------------
    Mylight.use();
    Mylight.setInt("material.diffuse", 0);
    Mylight.setInt("material.specular", 1);


    while (!glfwWindowShouldClose(window))
    {//一切变换都是局部坐标!
        auto cur_time = glfwGetTime();
        //input
        processInput(window);

        //清除所有信息
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        Mylight.use();
        Mylight.setVec3("viewPos", camera.Position);
        Mylight.setFloat("material.shininess", 32.0f);

        // directional light
        Mylight.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f);
        Mylight.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f);
        Mylight.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);
        Mylight.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);
        // point light 1
        Mylight.setVec3("pointLights.position", glm::vec3(0.0f, 3.0f, 0.0f));
        Mylight.setVec3("pointLights.ambient", 0.05f, 0.05f, 0.05f);
        Mylight.setVec3("pointLights.diffuse", 0.8f, 0.8f, 0.8f);
        Mylight.setVec3("pointLights.specular", 1.0f, 1.0f, 1.0f);
        Mylight.setFloat("pointLights.constant", 1.0f);
        Mylight.setFloat("pointLights.linear", 0.09);
        Mylight.setFloat("pointLights.quadratic", 0.032);
        
        // spotLight
        Mylight.setVec3("spotLight.position", camera.Position);
        Mylight.setVec3("spotLight.direction", camera.Front);
        Mylight.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f);
        Mylight.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f);
        Mylight.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);
        Mylight.setFloat("spotLight.constant", 1.0f);
        Mylight.setFloat("spotLight.linear", 0.09);
        Mylight.setFloat("spotLight.quadratic", 0.032);
        Mylight.setFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f)));
        Mylight.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(15.0f)));     

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        Mylight.setMat4("projection", projection);
        Mylight.setMat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.0f);
        Mylight.setMat4("model", model);


        glm::mat4 model_body = glm::mat4(1.0f);
        glm::mat4 model_head = glm::mat4(1.0f);
        glm::mat4 model_left_arm = glm::mat4(1.0f);
        glm::mat4 model_right_arm = glm::mat4(1.0f);
        glm::mat4 model_left_leg = glm::mat4(1.0f);
        glm::mat4 model_right_leg = glm::mat4(1.0f);

        Mylight.use();
        Mylight.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
        Mylight.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
        Mylight.setVec3("lightPos", lightPos);
        Mylight.setVec3("viewPos", camera.Position);



        // point light 3
        Mylight.setVec3("pointLights[2].position", glm::vec3(0.0f, 2.0f, 0.0f));
        Mylight.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f);
        Mylight.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f);
        Mylight.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f);
        Mylight.setFloat("pointLights[2].constant", 1.0f);
        Mylight.setFloat("pointLights[2].linear", 0.09);
        Mylight.setFloat("pointLights[2].quadratic", 0.032);

        /*脑袋*/
        model_head = glm::translate(model_head, glm::vec3(0.0f, (body_size.y + head_size.y), 0.0f));
        //model_head = glm::rotate(model_head, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
        model_head = glm::scale(model_head, head_size);
        int modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_head));
        renderingObj_pct(vertex_array, texture2);


        /*身体*/
        //model_body = glm::scale(model_body, body_size);
        modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_body));
        renderingObj_pct(vertex_array, texture1);

        /**右手*/
        //先平移到指定位置
        model_left_arm = glm::translate(model_left_arm, glm::vec3((body_size.x + arm_size.x / 2), 0.0f , 0.0f));
        //然后进行（绕矩形的一边）旋转（需要转变转轴）
        model_left_arm = glm::translate(model_left_arm,glm::vec3(0.0f, (arm_size.y / 2) / arm_size.y, 0.0f));
        model_left_arm = glm::rotate(model_left_arm,  (float)(sin(cur_time) + glm::radians(60.0f)), glm::vec3(0.0f, 0.0f, 1.0f));
        model_left_arm = glm::translate(model_left_arm,glm::vec3(0.0f, - (arm_size.y / 2) / arm_size.y, 0.0f));
        //最后进行缩放（不能一开始就缩放，局部坐标系的变换会影响到后续的变换）
        model_left_arm = glm::scale(model_left_arm, arm_size);
        modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_left_arm));
        renderingObj_pct(vertex_array, texture1);

        /**左手*/
        model_right_arm = glm::translate(model_right_arm, glm::vec3((- body_size.x - arm_size.x / 2), 0.0f , 0.0f));
        model_right_arm = glm::translate(model_right_arm,glm::vec3(0.0f, (arm_size.y / 2) / arm_size.y, 0.0f));
        model_right_arm = glm::rotate(model_right_arm,  (float)(-sin(cur_time) - glm::radians(60.0f)), glm::vec3(0.0f, 0.0f, 1.0f));
        model_right_arm = glm::translate(model_right_arm,glm::vec3(0.0f, - (arm_size.y / 2) / arm_size.y, 0.0f));
        model_right_arm = glm::scale(model_right_arm, arm_size);

        modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_right_arm));
        renderingObj_pct(vertex_array, texture1);


        /**右腿*/
        model_right_leg = glm::translate(model_right_leg, glm::vec3((body_size.x / 3), -(body_size.y + arm_size.y), 0.0f));
        model_right_leg = glm::translate(model_right_leg,glm::vec3(0.0f, (arm_size.y / 2) / arm_size.y, 0.0f));
        model_right_leg = glm::rotate(model_right_leg,  (float)(sin(cur_time*2) - glm::radians(20.0f)), glm::vec3(1.0f, 0.0f, 0.0f));
        model_right_leg = glm::translate(model_right_leg,glm::vec3(0.0f, - (arm_size.y / 2) / arm_size.y, 0.0f));
        model_right_leg = glm::scale(model_right_leg, arm_size);

        modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_right_leg));
        renderingObj_pct(vertex_array, texture1);

        /**左腿*/
        model_left_leg = glm::translate(model_left_leg, glm::vec3(-(body_size.x / 3), -(body_size.y + arm_size.y), 0.0f));
        model_left_leg = glm::translate(model_left_leg,glm::vec3(0.0f, (arm_size.y / 2) / arm_size.y, 0.0f));
        model_left_leg = glm::rotate(model_left_leg,  (float)(-sin(cur_time*2) - glm::radians(20.0f)), glm::vec3(1.0f, 0.0f, 0.0f));
        model_left_leg = glm::translate(model_left_leg,glm::vec3(0.0f, - (arm_size.y / 2) / arm_size.y, 0.0f));
        model_left_leg = glm::scale(model_left_leg, arm_size);

        modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model_left_leg));
        renderingObj_pct(vertex_array, texture1);


        glfwSwapBuffers(window);
        /**checks if any events are triggered*/
        glfwPollEvents();  
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    // glDeleteVertexArrays(1, &vertex_array);
    // glDeleteBuffers(1, &vertex_buffer);
    // glDeleteBuffers(1, &element_buffer);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}


// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}


void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}
